<?php
require_once 'Snoopy.class.php';
require_once '../util/data_digest.lib.php';
require_once '../util/webmethod.lib.php';
/** 
 * 酷武公司接口[简称kuwu]API。
 * @author alex  
 * @version v1.0  2010-08-14
 */

class KuWuRequest {
	/**
	 * @var string $host_url API的网站地址
	 */	
	var $host_url='http://www.smtkw.com/';
	
	/**
	 * @var array $app_token 应用app访问API的令牌，参数包括：<br/>
	 * string app_myprivatekey : 必须， 我方的私钥.<br/>
	 * string app_kuwupublickey :必须， 酷武公司的公钥<br/>
	 * string app_myprivatekey_pwd   :可选， 我方私钥的密码，默认:NULL。
	 * boolean app_key_is_file : 可选， key是否是文件，默认:false。
	 */
	var $app_token=array();
	
	/**
	 * @var array $app_setting 应用app访问API的通用参数设置，一般不改变默认值，包括：<br/>
	 * strng  sign_method :签名方法，dss1。<br/>
	 * string v：API接口版本号，v1.0<br/>
	 */
	var $app_setting=array('sign_method'=>OPENSSL_ALGO_DSS1, 
	                       'v'=>'v1.0');
	private $privkey=NULL;
	private $pubkey=NULL;
	/**
	 * 类构造函数
	 * @param array $app_token 应用app访问API的令牌，参见@see $app_token。
	 * 如果不传，签名、验证数字签名时总是动态生成key resource；如果传入令牌，将预先生成key resource，签名、验证数字签名速度较快，
	 * 如果需要改变访问API的令牌，改变属性@see app_token的值后，再调用函数@see refreshKey刷新key resource。
	 */
	function __construct($app_token=NULL){
	  if(isset($app_token) && is_array($app_token)){
	     $this->app_token=$app_token;
	     $this->refreshKey();
		}  		
	}
	function __destruct(){
		if($this->privkey) openssl_free_key($this->privkey);
		if($this->pubkey) openssl_free_key($this->pubkey);
	}
	function refreshKey(){
	     $keyisfile=$this->app_token['app_key_is_file'] ? true : false;
	     $pwd=$this->app_token['app_myprivatekey_pwd'] ? $this->app_token['app_myprivatekey_pwd'] : NULL;
	     $privkey=$this->app_token['app_myprivatekey'];
         if($keyisfile){
        	if(file_exists($privkey))
        	  $privkey=file_get_contents($privkey);
        	else
        	  throw new Exception('找不到私钥文件,' . $privkey);
          }
          
          if($this->privkey) openssl_free_key($this->privkey);
          if(isset($pwd))
            $this->privkey=openssl_pkey_get_private($privkey,$pwd);
          else 
            $this->privkey=openssl_pkey_get_private($privkey);
          if(! $this->privkey)
            throw new Exception('得不到私钥 ,' . openssl_error_string()); 
          
		  $pubkey=$this->app_token['app_kuwupublickey'];
          if($keyisfile){
          	if(file_exists($pubkey))
          	  $pubkey=file_get_contents($pubkey);
        	else
        	  throw new Exception('找不到公钥文件,' . $pubkey);
          }
          
          if($this->pubkey) openssl_free_key($this->pubkey);
          $this->pubkey=openssl_pkey_get_public($pubkey);
          if(! $this->pubkey)
            throw new Exception('得不到公钥 ,' . openssl_error_string()); 
	}
	/**
	 * 接受API的返回字符串。
	 * @param array $kuwu_req_array 通过$_REQUEST得到的参数数组。
	 * @return array|bool 调用的api接口得到的字符串。
	 */	    
    function recv(array $kuwu_req_array){
 		if(! $this->app_token['app_kuwupublickey'] || ! $this->app_setting['sign_method']) 
		   return false; 
		   
		
		$kuwu_xml=$kuwu_req_array['LOGISTICS_INTERFACE'];
		$kuwu_digest=$kuwu_req_array['DATA_DIGEST'];
		$kuwu_digest=fromDotNETDSS1Signation(base64_decode($kuwu_digest));
		if($this->pubkey){
			 $result=openssl_verify($kuwu_xml,$kuwu_digest,$this->pubkey,$this->app_setting['sign_method']);
             if($result===-1)  throw new Exception('验证失败 ,' . openssl_error_string());	
            $verifyresult=$result>0;	 
		}
		else{
		  $keyisfile=$this->app_token['app_key_is_file'] ? true : false;
		  $verifyresult=data_digest_verify($kuwu_xml,$kuwu_digest,$this->app_token['app_kuwupublickey'],$keyisfile,$this->app_setting['sign_method']);
		}
		if(!$verifyresult)  throw new Exception('签名验证失败 ,' . openssl_error_string());	
		return iconv('GBK','UTF-8',$kuwu_xml);
    }
    
	/**
	 * 得到API的返回字符串。
	 * @param string $kuwu_xml 要调用的api接口的参数xml，一般通用的参数不要传。
	 * @return string|bool 调用的api接口得到的字符串。
	 */
	function get($kuwu_xml){
		if(! $this->app_token['app_myprivatekey'] || ! $this->app_setting['sign_method']) 
		   return false;
		
		$kuwu_xml=iconv('UTF-8','GBK//IGNORE',$kuwu_xml);
		$kuwu_type=iconv('UTF-8','GBK',$this->app_setting['v']);
		
		//sign xml
		if($this->privkey){
            $result=openssl_sign($kuwu_xml,$signdata,$this->privkey,$this->app_setting['sign_method']);
           if(!$result)  throw new Exception('签名失败 ,' . openssl_error_string());
		}else{
		  $pwd=$this->app_token['app_myprivatekey_pwd'] ? $this->app_token['app_myprivatekey_pwd'] : NULL;
		  $keyisfile=$this->app_token['app_key_is_file'] ? true : false;
		  $signdata=data_digest_sign($kuwu_xml,$this->app_token['app_myprivatekey'],$keyisfile,$this->app_setting['sign_method'],$pwd);
		}
		$kuwu_digest=base64_encode(toDotNETDSS1Signation($signdata));
		//prepare param
		$host_url = $this->host_url;
		$kuwu_param=array('logistics_interface'=>$kuwu_xml,'data_digest'=>$kuwu_digest,'type'=>$kuwu_type);
		
		//call  method
        $snoopy = new Snoopy();
        $snoopy->accept='text/xml';
        $snoopy->submit($host_url,$kuwu_param); 
        
        if($snoopy->error)  throw new Exception($snoopy->error);
        return iconv('GBK','UTF-8//IGNORE',$snoopy->results);
	}
	
	/**
	 * 得到API的返回值，xml生成的数组。
	 * @param array $paramArr 要调用的api接口的参数数组。
	 * @return array|bool 调用的api接口得到的数组。
	 */	
	function getArray(array $paramArr){
		    $kuwu_xml=webmethod_array2xml($paramArr);
			$result=$this->get($kuwu_xml);
			$arr=array(); 
			if(! webmethod_xml2array($result,$arr)) return false;
			return $arr;
	}	
	/**
	 * 接受API的返回值，xml生成的数组。
	 * @param array $kuwu_req_array 通过$_REQUEST得到的参数数组。
	 * @return array|bool 调用的api接口得到的数组。
	 */	
	function recvArray(array $kuwu_req_array){
			$result=$this->recv($kuwu_req_array);
			$arr=array(); 
			if(! webmethod_xml2array($result,$arr)) return false;
			return $arr;
	}	
	/**
	 * 检查接口调用get*函数返回是否有错误
	 * @param  mixed $result  接口调用get*函数返回值.
	 * @return bool true 接口调用有效，false 接口调用出错.
	 */
	var $result_error_string=array(
     'S01'=>'非法的XML格式',
     'S02'=>'非法的数字签名',
     'S03'=>'非法的仓储公司',
     'S04'=>'非法的通知类型',
     'S05'=>'非法的通知状态',
     'S07'=>'系统异常请重试',
     'S08'=>'非法的仓库编号',
     'S09'=>'非法的订单编号',
     'S10'=>'非法的物流公司',
     'S11'=>'非法的商户编号',
     'S12'=>'非法的产品编号'	
	);
	function checkResult($result){
		if(! array_key_exists('Response',$result))
		  throw new Exception('标准返回格式错误，找不到Response');
		$reponse=$result['Response'];
		if($reponse['success']==='true') return true;
		$reason=$reponse['reason'];
		if($reason)  throw new Exception('API调用失败，'.$this->result_error_string[$reason] .'['.$reason.']');
		else throw new Exception('API调用失败');
	}


}

